use crate::{GraphConfig, QueryResult, Result, NodeId, EdgeId, NodeType, EdgeType, SourceLocation};
use std::path::Path;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

/// Main system interface for CodeGraph-RT
pub struct GraphSystem {
    config: GraphConfig,
    /// Current project graph
    project_graph: Option<ProjectGraph>,
}

impl GraphSystem {
    /// Create a new graph system with default configuration
    pub async fn new() -> Result<Self> {
        Self::with_config(GraphConfig::default()).await
    }

    /// Create a new graph system with custom configuration
    pub async fn with_config(config: GraphConfig) -> Result<Self> {
        Ok(Self {
            config,
            project_graph: None,
        })
    }

    /// Start the HTTP server
    pub async fn start_server(&mut self, host: &str, port: u16) -> Result<()> {
        tracing::info!("Starting server on {}:{}", host, port);
        // TODO: Implement server startup
        Ok(())
    }

    /// Build a complete graph for a project
    pub async fn build_project_graph(
        &mut self,
        project_path: &Path,
        _languages: Option<&str>,
    ) -> Result<ProjectGraph> {
        tracing::info!("Building graph for project: {:?}", project_path);
        // TODO: Implement project graph building
        let graph = ProjectGraph::new();
        self.project_graph = Some(graph.clone());
        Ok(graph)
    }

    /// Watch a project for changes and update incrementally
    pub async fn watch_project(
        &self,
        project_path: &Path,
        _storage_path: Option<&Path>,
    ) -> Result<()> {
        tracing::info!("Watching project: {:?}", project_path);
        // TODO: Implement project watching
        Ok(())
    }

    /// Save a graph to storage
    pub async fn save_graph(&self, _graph: &ProjectGraph, output_path: &Path) -> Result<()> {
        tracing::info!("Saving graph to: {:?}", output_path);
        // TODO: Implement graph saving
        Ok(())
    }

    /// Query an existing graph
    pub async fn query_graph(&self, _storage_path: &Path, query: &str) -> Result<QueryResult> {
        tracing::info!("Querying graph with: {}", query);
        // TODO: Implement graph querying
        Ok(QueryResult {
            nodes: Vec::new(),
            edges: Vec::new(),
            metadata: std::collections::HashMap::new(),
            execution_time_ms: 0,
        })
    }

    /// Get the current project graph (read-only)
    pub fn get_project_graph(&self) -> Result<&ProjectGraph> {
        self.project_graph.as_ref()
            .ok_or_else(|| crate::CodeGraphError::graph_update_error("No project graph available"))
    }

    /// Get the current project graph (mutable)
    pub fn get_project_graph_mut(&mut self) -> Result<&mut ProjectGraph> {
        self.project_graph.as_mut()
            .ok_or_else(|| crate::CodeGraphError::graph_update_error("No project graph available"))
    }
}

/// Simple node representation for ProjectGraph
#[derive(Debug, Clone)]
pub struct SimpleNode {
    pub id: NodeId,
    pub node_type: NodeType,
    pub name: String,
    pub language: String,
    pub location: SourceLocation,
}

/// Simple edge representation for ProjectGraph
#[derive(Debug, Clone)]
pub struct SimpleEdge {
    pub id: EdgeId,
    pub source: NodeId,
    pub target: NodeId,
    pub edge_type: EdgeType,
}

/// Represents a complete project graph
#[derive(Debug, Clone)]
pub struct ProjectGraph {
    nodes: HashMap<NodeId, SimpleNode>,
    edges: HashMap<EdgeId, SimpleEdge>,
}

impl ProjectGraph {
    pub fn new() -> Self {
        Self {
            nodes: HashMap::new(),
            edges: HashMap::new(),
        }
    }

    pub fn node_count(&self) -> usize {
        self.nodes.len()
    }

    pub fn edge_count(&self) -> usize {
        self.edges.len()
    }

    pub fn languages(&self) -> Vec<String> {
        let mut languages: Vec<String> = self.nodes.values()
            .map(|node| node.language.clone())
            .collect::<std::collections::HashSet<_>>()
            .into_iter()
            .collect();
        languages.sort();
        languages
    }

    /// Get a node by ID
    pub fn get_node(&self, node_id: NodeId) -> Option<Arc<RwLock<SimpleNode>>> {
        self.nodes.get(&node_id).map(|node| Arc::new(RwLock::new(node.clone())))
    }

    /// Remove a node from the graph
    pub fn remove_node(&mut self, node_id: NodeId) -> bool {
        if self.nodes.remove(&node_id).is_some() {
            // Also remove any edges connected to this node
            let edges_to_remove: Vec<EdgeId> = self.edges.iter()
                .filter(|(_, edge)| edge.source == node_id || edge.target == node_id)
                .map(|(id, _)| *id)
                .collect();

            for edge_id in edges_to_remove {
                self.edges.remove(&edge_id);
            }
            true
        } else {
            false
        }
    }

    /// Get all edges in the graph
    pub fn edges(&self) -> Vec<SimpleEdge> {
        self.edges.values().cloned().collect()
    }

    /// Get an edge by ID
    pub fn get_edge(&self, edge_id: EdgeId) -> Option<Arc<RwLock<SimpleEdge>>> {
        self.edges.get(&edge_id).map(|edge| Arc::new(RwLock::new(edge.clone())))
    }

    /// Remove an edge from the graph
    pub fn remove_edge(&mut self, edge_id: EdgeId) -> bool {
        self.edges.remove(&edge_id).is_some()
    }
}

impl Default for ProjectGraph {
    fn default() -> Self {
        Self::new()
    }
}
